package org.ihelpuoo.parser.segment;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.ihelpuoo.io.CharInputStream;
import org.ihelpuoo.io.TextTokenReader;
import org.ihelpuoo.lang.LoaderException;
import org.ihelpuoo.lang.RenderException;
import org.ihelpuoo.parser.AbstractTextEngine;
import org.ihelpuoo.parser.StringTraveler;

/**
 * <b>Segment class:</b>
 * <hr color=grey size=1>
 *
 * @ TODO ZZH: consider about CharSquance. maybe you need TextParser
 *
 * @author zozoh  created @ 2006
 *
 */
public class CharSegment extends AbstractTextEngine implements Segment
{
	private MarkStack						markStack;
	private List<SegmentNode>				children;
	private Map<String, List<PlugPoint>>	ppMap;
	private List<CharSegment>				childSegments;
	private Marks							marks;
	private String							_s;

	public CharSegment()
	{
		marks = Marks.instance();
		markStack = new MarkStack();
	}

	public CharSegment(Reader r) throws LoaderException
	{
		this();
		parse(r);
	}

	public CharSegment(InputStream ins) throws LoaderException
	{
		this();
		parse(ins);
	}

	public CharSegment(CharSequence cs)
	{
		this();
		try
		{
			parse(new CharInputStream(cs));
		}
		catch (LoaderException e)
		{
			throw new RuntimeException(e);
		}
	}

	/**
	 * @return Returns the markStack.
	 */
	protected MarkStack getMarkStack()
	{
		return markStack;
	}

	public void parse(Reader r) throws LoaderException
	{
		children = new LinkedList<SegmentNode>();
		ppMap = new HashMap<String, List<PlugPoint>>();
		childSegments = new LinkedList<CharSegment>();
		markStack.clear();
		if (null == r)
			return;
		StringTraveler st;
		try
		{
			TextTokenReader ttr = new TextTokenReader(r);
			_s = ttr.readAll();
			st = new StringTraveler(_s);
		}
		catch (IOException e)
		{
			throw new LoaderException(e);
		}
		int i = 0;
		while (!st.isEnd())
		{
			SegmentNode node = generateNode(st);
			node.setSegment(this);
			try
			{
				node.valueOf(st);
				children.add(i++,node);
			}
			catch (NodeIsUnusefulExcption e)
			{
			}
			if (node instanceof PlugPoint)
			{
				String key = ((PlugPoint)node).getName();
				List<PlugPoint> list = ppMap.get(key);
				if (null == list)
				{
					list = new LinkedList<PlugPoint>();
					list.add((PlugPoint)node);
					ppMap.put(key,list);
				}
				else
				{
					list.add((PlugPoint)node);
				}
			}
		}

	}

	public Segment duplicate()
	{
		CharSegment newSegment = new CharSegment();
		newSegment.children = new ArrayList<SegmentNode>(this.children.size());
		newSegment.ppMap = new HashMap<String, List<PlugPoint>>();
		newSegment.childSegments = new LinkedList<CharSegment>();
		Iterator it = this.children.iterator();
		while (it.hasNext())
		{
			SegmentNode node = ((SegmentNode)it.next()).duplicate();
			node.setSegment(newSegment);
			newSegment.children.add(node);
			if (node instanceof PlugPoint)
			{
				((PlugPoint)node).clear();
				String key = ((PlugPoint)node).getName();
				List<PlugPoint> list = newSegment.ppMap.get(key);
				if (null == list)
				{
					list = new LinkedList<PlugPoint>();
					list.add((PlugPoint)node);
					newSegment.ppMap.put(key,list);
				}
				else
				{
					list.add((PlugPoint)node);
				}
			}
		}
		newSegment._s = _s;
		return newSegment;
	}

	public boolean isEquals(Segment seg)
	{
		if (null == seg)
			return false;
		return seg.getOriginContent().equals(getOriginContent());
	}

	private SegmentNode generateNode(StringTraveler st) throws LoaderException
	{
		SegmentNode node = null;
		if (st.hasNext(marks.markHead()))
		{
			st.next(marks.markHead().length);
			for (int i = 0; i < marks.markHead().length; i++)
			{
				markStack.push(marks.markHead()[i]);
			}
			//
			// If the "st" isEnd(), that's mean the Segment is end by "${", it will cause
			// syntax error.
			//
			if (st.isEnd())
			{
				markStack.clear();
				throw new LoaderException("Syntax Error: you segment string can not end by "
						+ String.valueOf(marks.markHead()));
			}
			node = new PlugPoint();
			node.setMarks(marks);
		}
		else
		{
			node = new StringNode();
			node.setMarks(marks);
		}
		return node;
	}

	protected void registSubPlugPointMap(CharSegment segment)
	{
		childSegments.add(segment);
	}

	public void clearAll()
	{
		Iterator it = ppMap.values().iterator();
		while (it.hasNext())
		{
			Iterator it2 = ((List)it.next()).iterator();
			while (it2.hasNext())
				((PlugPoint)it2.next()).clear();
		}

		this.childSegments.clear();
	}

	public List<PlugPoint> getPlugPoints()
	{
		LinkedList<PlugPoint> list = new LinkedList<PlugPoint>();
		Iterator<List<PlugPoint>> it = ppMap.values().iterator();
		while (it.hasNext())
			list.addAll(it.next());
		return list;
	}

	public Segment set(String key, Object v)
	{
		if (null == v)
			return this;
		//
		// Firstly, check it child Segments can admit the key or not
		//
		Iterator<CharSegment> it = childSegments.iterator();
		while (it.hasNext())
			it.next().set(key,v);
		//
		// Secondly, check self can admit the key or not.
		//
		List<PlugPoint> list = ppMap.get(key);
		if (null != list && list.size() > 0)
		{
			Iterator<PlugPoint> ppIt = list.iterator();
			while (ppIt.hasNext())
			{
				ppIt.next().setValue(v);
			}
		}
		return this;
	}

	public boolean contains(String key)
	{
		return ppMap.containsKey(key);
	}

	public Segment add(String key, Object v)
	{
		if (null == v)
			return this;
		//
		// Firstly, check it child Segments can admit the key or not
		//
		Iterator<CharSegment> it = childSegments.iterator();
		while (it.hasNext())
			it.next().set(key,v);
		//
		// Secondly, check self can admit the key or not.
		//
		List<PlugPoint> list = ppMap.get(key);
		if (null != list && list.size() > 0)
		{
			Iterator<PlugPoint> ppIt = list.iterator();
			while (ppIt.hasNext())
				ppIt.next().addValue(v);
		}
		return this;
	}

	public Segment set(String key, int v)
	{
		return set(key,String.valueOf(v));
	}

	public Segment set(String key, boolean v)
	{
		return set(key,String.valueOf(v));
	}

	public Segment set(String key, byte v)
	{
		return set(key,String.valueOf(v));
	}

	public Segment set(String key, double v)
	{
		return set(key,String.valueOf(v));
	}

	public Segment set(String key, float v)
	{
		return set(key,String.valueOf(v));
	}

	public Segment set(String key, long v)
	{
		return set(key,String.valueOf(v));
	}

	public Segment set(String key, short v)
	{
		return set(key,String.valueOf(v));
	}

	public int size()
	{
		if (null == ppMap)
			return 0;
		return ppMap.size();
	}

	public Iterator<String> iterator()
	{
		return ppMap.keySet().iterator();
	}

	public int itemNumber()
	{
		if (null == children)
			return 0;
		return children.size();
	}

	@Override
	protected Object clone() throws CloneNotSupportedException
	{
		return this.duplicate();
	}

	@Override
	public boolean equals(Object obj)
	{
		if (obj instanceof Segment)
			return this.isEquals((Segment)obj);
		return false;
	}

	public String getOriginContent()
	{
		return _s;
	}

	public CharSequence render()
	{
		StringBuffer sb = new StringBuffer();
		Iterator it = children.iterator();
		while (it.hasNext())
			sb.append(((SegmentNode)it.next()).evaluate());

		return sb;
	}

	@Override
	public String toString()
	{
		return render().toString();
	}

	public void render2(Writer w) throws RenderException
	{
		try
		{
			w.append(toString());
		}
		catch (IOException e)
		{
			throw new RenderException(e);
		}
	}

}
